#ifndef MOREFLOW_HH
#define MOREFLOW_HH
#include <click/ipaddress.hh>
#include <click/vector.hh>
#include <click/dequeue.hh>
#include <click/packet.hh>
#include <click/etheraddress.hh>
#include <click/timer.hh>
#include <click/ewma.hh>
#include "morebatch.hh"
#include <unistd.h>

CLICK_DECLS

/**
 * The rules are the following:
 * 1) when the flow's cum_ack increases, and node is on ack path, go PENDING
 * 2) on pull, if PENDING, make and go SENT
 * 3) on push, from ack_next cum_ack >= our cum_ack, go NONE
 * 4) on TXf for ACK and SENT
 *   4.1) FAILED -> go PENDING
 *   4.2) SUCCESS -> go NONE
 */

typedef enum {
    NONE = 0,   // everything is up-to-date
    PENDING,    // need to propagate new cum_ack
    SENT,       // waiting for TXf
} MOREAckFlag;

typedef uint16_t more_ack_metric;

/**
 * This is what we store per flow
 */
class MOREFlow {
    public: // FIXME exposure
        MOREBatch * _batch;
    protected:
        uint _seqno; // the batch in progress
        uint32_t _dstmap; // for each dst 1 indicates it's still active

        typedef Vector<float*> floatVec;
        typedef Vector<float*>::iterator floatIt;
        // for each teacher, for each dst have the expected number of packets heard
        floatVec _inflows;
        // and have one for oneself
        float _outflow[MAX_DST];

    // create new batch, init using bcfg
        MOREBatch *create(uint seqno);

    public:
    // config for the NEXT batch
        MOREBatch::Config bcfg;

    // flow identification
        bool src; // am I the source
        int dst; // if >= 0 I am the "dst" index in dstmap
        int src_dstmap; // full dstmap set at source

        float creditf; // credit factor

    // end game
        bool stopped;

    // ack routing
        int ack_rate; // rate for ACK packets
        EtherAddress ack_next; // next hop 

    // acking state
        MOREAckFlag ackflag;

        MOREFlow() :
            _batch(0),
            _seqno(0),_dstmap(0),
            src(false), dst(-1),
            creditf(0.0),
            stopped(true),
            ack_rate(0),
            ackflag(NONE)
        {}
        void reset(){
            _inflows.clear();
            delete _batch; _batch = 0;
    
            ack_rate = 0;
            src = false; dst = -1;
            _seqno = _dstmap = 0;
            ackflag = NONE;
            creditf = 0.0;
        }
        ~MOREFlow() {
            reset();
        }
    
        void stop() {
            reset();
            stopped = true;
        }

    // source a single batch
        void source_batch();

    // NOTE: you can do the check whenever you want
        bool source_check() {
            if(src && (_dstmap == 0 || !_batch)) {
                source_batch();
                assert(_batch);
                return true;
            }
            return false;
        }

        void set_outflow(Vector<float> &zp);
        void add_inflow(Vector<float> &zp);
    
        bool update_dstmap(uint32_t newmap); // true if changed, it updates creditf too

    // update cum_ack and flush old batches
        void flush(uint cum_ack);
    
        uint seqno() const { return _seqno; }
        uint32_t dstmap() const { return _dstmap; }

    // lookup OR create if force = True
        MOREBatch *lookup(uint seqno, bool force = false);
    
        bool queued() const { 
            // check if the next encode() would return non 0
            return _dstmap > 0 && _batch && _batch->queued();
        }
    
        WritablePacket *encode();
        WritablePacket *make_ack();
};

CLICK_ENDDECLS
#endif // MOREFLOW_HH

